home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / JComboBox.java < prev    next >
Text File  |  1998-06-30  |  44KB  |  1,319 lines

  1. /*
  2.  * @(#)JComboBox.java    1.41 98/04/10
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing;
  21.  
  22. import java.beans.*;
  23. import java.util.*;
  24. import java.awt.*;
  25. import java.awt.event.*;
  26. import com.sun.java.swing.event.*;
  27. import com.sun.java.swing.plaf.*;
  28. import java.io.Serializable;
  29. import com.sun.java.swing.border.*;
  30. import com.sun.java.accessibility.*;
  31.  
  32. /**
  33.  * Swing's implementation of a ComboBox -- a combination of a text field and 
  34.  * drop-down list that lets the user either type in a value or select it from 
  35.  * a list that is displayed when the user asks for it. The editing capability 
  36.  * can also be disabled so that the JComboBox acts only as a drop down list.
  37.  * <p>
  38.  * For the keyboard keys used by this component in the standard Look and
  39.  * Feel (L&F) renditions, see the
  40.  * <a href="doc-files/Key-Index.html#JComboBox">JComboBox</a> key assignments.
  41.  * <p>
  42.  * Warning: serialized objects of this class will not be compatible with
  43.  * future swing releases.  The current serialization support is appropriate 
  44.  * for short term storage or RMI between Swing1.0 applications.  It will
  45.  * not be possible to load serialized Swing1.0 objects with future releases
  46.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  47.  * baseline for the serialized form of Swing objects.
  48.  *
  49.  * @beaninfo
  50.  *   attribute: isContainer false
  51.  *
  52.  * @version 1.41 04/10/98
  53.  * @author Arnaud Weber
  54.  */
  55. public class JComboBox extends JComponent implements ItemSelectable,ListDataListener,ActionListener, Accessible {
  56.     protected ComboBoxModel    dataModel;
  57.     protected ListCellRenderer renderer;
  58.     protected ComboBoxEditor       editor;
  59.     protected int maximumRowCount = 8;
  60.     protected boolean isEditable  = false;
  61.     protected Object selectedItemReminder = null;
  62.     protected KeySelectionManager keySelectionManager = null;
  63.     protected String actionCommand = "comboBoxChanged";
  64.     protected boolean lightWeightPopupEnabled = true;
  65.  
  66.     /**
  67.      * Creates a JComboBox that takes its items from an existing ComboBoxDataModel.
  68.      *
  69.      * @param aModel the ComboBoxModel that provides the displayed list of items
  70.      */
  71.     public JComboBox(ComboBoxModel aModel) {
  72.         super();
  73.         setModel(aModel);
  74.         installAncestorListener();
  75.         updateUI();
  76.     }
  77.  
  78.     /** 
  79.      * Creates a JComboBox that contains the elements in the specified array.
  80.      */
  81.     public JComboBox(final Object items[]) {
  82.         super();
  83.         setModel(new DefaultComboBoxModel(items));
  84.         installAncestorListener();
  85.         updateUI();
  86.     }
  87.  
  88.     /**
  89.      * Creates a JComboBox that contains the elements in the specified Vector.
  90.      */
  91.     public JComboBox(Vector items) {
  92.         super();
  93.         setModel(new DefaultComboBoxModel(items));
  94.         installAncestorListener();
  95.         updateUI();
  96.     }
  97.  
  98.     /**
  99.      * Creates a JComboBox with a default data model.
  100.      * The default data model is an empty list of objects. 
  101.      * Use <code>addItem</code> to add items.
  102.      */
  103.     public JComboBox() {
  104.         super();
  105.         setModel(new DefaultComboBoxModel());
  106.         installAncestorListener();
  107.         updateUI();
  108.     }
  109.  
  110.     protected void installAncestorListener() {
  111.         addAncestorListener(new AncestorListener(){
  112.                             public void ancestorAdded(AncestorEvent event){ hidePopup();}
  113.                            public void ancestorRemoved(AncestorEvent event){ hidePopup();}
  114.                            public void ancestorMoved(AncestorEvent event){ hidePopup();}});
  115.     }
  116.  
  117.     /**
  118.      * Sets the L&F object that renders this component.
  119.      *
  120.      * @param ui  the ComboBoxUI L&F object
  121.      * @see UIDefaults#getUI
  122.      *
  123.      * @beaninfo
  124.      *       expert: true
  125.      *  description: The ComboBoxUI implementation that defines the combo box look and feel.
  126.      */
  127.     public void setUI(ComboBoxUI ui) {
  128.         super.setUI(ui);
  129.     }
  130.  
  131.     /**
  132.      * Notification from the UIFactory that the L&F has changed. 
  133.      *
  134.      * @see JComponent#updateUI
  135.      */
  136.     public void updateUI() {
  137.         setUI((ComboBoxUI)UIManager.getUI(this));
  138.     }
  139.  
  140.  
  141.     /**
  142.      * Returns the name of the L&F class that renders this component.
  143.      *
  144.      * @return "ComboBoxUI"
  145.      * @see JComponent#getUIClassID
  146.      * @see UIDefaults#getUI
  147.      */
  148.     public String getUIClassID() {
  149.         return "ComboBoxUI";
  150.     }
  151.  
  152.  
  153.     /**
  154.      * Returns the L&F object that renders this component.
  155.      *
  156.      * @return the ComboBoxUI object that renders this component
  157.      */
  158.     public ComboBoxUI getUI() {
  159.         return (ComboBoxUI)ui;
  160.     }
  161.  
  162.     /**
  163.      * Sets the data model that the JComboBox uses to obtain the list of items.
  164.      *
  165.      * @param aModel the ComboBoxModel that provides the displayed list of items
  166.      * 
  167.      * @beaninfo
  168.      *        bound: true
  169.      *  description: Model that the combo box uses to get data to display.
  170.      */
  171.     public void setModel(ComboBoxModel aModel) {
  172.         ComboBoxModel oldModel = dataModel;
  173.         if ( dataModel != null )
  174.             dataModel.removeListDataListener(this);
  175.         dataModel = aModel;
  176.         firePropertyChange( "model", oldModel, dataModel);
  177.         dataModel.addListDataListener(this);
  178.         invalidate();
  179.     }
  180.  
  181.     /**
  182.      * Returns the data model currently used by the JComboBox.
  183.      *
  184.      * @return the ComboBoxModel that provides the displayed list of items
  185.      */
  186.     public ComboBoxModel getModel() {
  187.         return dataModel;
  188.     }
  189.  
  190.     /*
  191.      * Properties
  192.      */
  193.  
  194.     /**
  195.      * When displaying the popup, JComboBox choose to use a light weight popup if
  196.      * it fits. This method allows you to disable this feature. You have to do disable
  197.      * it if your application mixes light weight and heavy weights components.
  198.      *
  199.      * @beaninfo
  200.      *       expert: true
  201.      *  description: When set, disables using light weight popups.
  202.      */
  203.     public void setLightWeightPopupEnabled(boolean aFlag) {
  204.         lightWeightPopupEnabled = aFlag;
  205.     }
  206.  
  207.     /**
  208.      * Returns true if lightweight (all-Java) popups are in use,
  209.      * or false if heavyweight (native peer) popups are being used.
  210.      *
  211.      * @return true if lightweight popups are in use
  212.      */
  213.     public boolean isLightWeightPopupEnabled() { 
  214.         return lightWeightPopupEnabled;
  215.     }
  216.  
  217.     /**
  218.      * Determines whether the JComboBox field is editable. An editable JComboBox
  219.      * allows the user to type into the field or selected an item from the list
  220.      * to initialize the field, after which it can be edited. (The editing affects
  221.      * only the field, the list item remains intact.) A non editable JComboBox 
  222.      * displays the selected item inthe field, but the selection cannot be modified.
  223.      *
  224.      * @param aFlag a boolean value, where true indicates that the field is editable
  225.      * 
  226.      * @beaninfo
  227.      *    preferred: true
  228.      *  description: If true, the user can type a new value in the combo box.
  229.      */
  230.     public void setEditable(boolean aFlag) {
  231.         boolean didChange = aFlag != isEditable;
  232.         isEditable = aFlag;
  233.         if ( didChange ) {
  234.             firePropertyChange( "editable", !isEditable, isEditable );
  235.         }
  236.     }
  237.  
  238.     /**
  239.      * Returns true if the JComboBox is editable.
  240.      * 
  241.      * @return true if the JComboBox is editable, else false
  242.      */
  243.     public boolean isEditable() {
  244.         return isEditable;
  245.     }
  246.  
  247.     /**
  248.      * Sets the maximum number of rows the JComboBox displays.
  249.      * If the number of objects in the model is greater than count,
  250.      * the combo box uses a scrollbar.
  251.      *
  252.      * @param count an int specifying the maximum number of items to display
  253.      *              in the list before using a scrollbar
  254.      * @beaninfo
  255.      *    preferred: true
  256.      *  description: The maximum number of rows the popup should have
  257.      */
  258.     public void setMaximumRowCount(int count) {
  259.         int oldCount = maximumRowCount;
  260.         maximumRowCount = count;
  261.         firePropertyChange( "maximumRowCount", oldCount, maximumRowCount );
  262.     }
  263.  
  264.     /**
  265.      * Returns the maximum number of items the combo box can display 
  266.      * without a scrollbar
  267.      *
  268.      * @return an int specifying the maximum number of items that are displayed
  269.      *         in the list before using a scrollbar
  270.      */
  271.     public int getMaximumRowCount() {
  272.         return maximumRowCount;
  273.     }
  274.  
  275.     /**
  276.      * Sets the renderer that paints the item selected from the list in
  277.      * the JComboBox field. The renderer is used if the JComboBox is not
  278.      * editable. If it is editable, the editor is used to render and edit
  279.      * the selected item.
  280.      * <p>
  281.      * The default renderer displays a string, obtained
  282.      * by calling the selected object's <code>toString</code> method.
  283.      * Other renderers can handle graphic images and composite items.
  284.      * <p>
  285.      * To display the selected item, <code>aRenderer.getListCellRendererComponent</code>
  286.      * is called, passing the list object and an index of -1.
  287.      *  
  288.      * @param aRenderer  the ListCellRenderer that displays the selected item.
  289.      * @see #setEditor
  290.      * @beaninfo
  291.      *     expert: true
  292.      *  description: The renderer that paints the item selected in the list.
  293.      */
  294.     public void setRenderer(ListCellRenderer aRenderer) {
  295.         ListCellRenderer oldRenderer = renderer;
  296.         renderer = aRenderer;
  297.         firePropertyChange( "renderer", oldRenderer, renderer );
  298.         invalidate();
  299.     }
  300.  
  301.     /**
  302.      * Returns the renderer used to display the selected item in the JComboBox
  303.      * field.
  304.      *  
  305.      * @return  the ListCellRenderer that displays the selected item.
  306.      */
  307.     public ListCellRenderer getRenderer() {
  308.         return renderer;
  309.     }
  310.  
  311.     /**
  312.      * Sets the editor used to paint and edit the selected item in the JComboBox
  313.      * field. The editor is used only if the receiving JComboBox is editable. 
  314.      * If not editable, the combo box uses the renderer to paint the selected item.
  315.      *  
  316.      * @param anEditor  the ComboBoxEditor that displays the selected item
  317.      * @see #setRenderer
  318.      * @beaninfo
  319.      *    expert: true
  320.      *  description: The editor that combo box uses to edit the current value
  321.      */
  322.     public void setEditor(ComboBoxEditor anEditor) {
  323.         ComboBoxEditor oldEditor = editor;
  324.  
  325.         if ( editor != null )
  326.             editor.removeActionListener(this);
  327.         editor = anEditor;
  328.         if ( editor != null ) {
  329.             editor.addActionListener(this);
  330.         }
  331.         firePropertyChange( "editor", oldEditor, editor );
  332.     }
  333.  
  334.     /**
  335.      * Returns the editor used to paint and edit the selected item in the JComboBox
  336.      * field.
  337.      *  
  338.      * @return the ComboBoxEditor that displays the selected item
  339.      */
  340.     public ComboBoxEditor getEditor() {
  341.         return editor;
  342.     }
  343.  
  344.     /*
  345.      * Selection
  346.      */
  347.     /** 
  348.      * Sets the selected item in the JComboBox by specifying the object in the list.
  349.      * If <code>anObject</code> is in the list, the list displays with 
  350.      * <code>anObject</code> selected. If the object does not exist in the list,
  351.      * the default data model selects the first item in the list.
  352.      *
  353.      * @param anObject  the list object to select
  354.      * @beaninfo
  355.      *    preferred:   true
  356.      *    description: Sets the selected item in the JComboBox.
  357.      */
  358.     public void setSelectedItem(Object anObject) {
  359.         dataModel.setSelectedItem(anObject);
  360.     }
  361.  
  362.     /**
  363.      * Returns the currently selected item.
  364.      *
  365.      * @return  the currently selected list object from the data model
  366.      */
  367.     public Object getSelectedItem() {
  368.         return dataModel.getSelectedItem();
  369.     }
  370.  
  371.     /**
  372.      * Selects the item at index <code>anIndex</code>.
  373.      *
  374.      * @param anIndex an int specifying the list item to select, where 0 specifies
  375.      *                the first item in the list
  376.      * @beaninfo
  377.      *   preferred: true
  378.      *  description: The item at index is selected.
  379.      */
  380.     public void setSelectedIndex(int anIndex) {
  381.         int size = dataModel.getSize();
  382.  
  383.         if ( anIndex < 0 || anIndex >= size )
  384.             throw new IllegalArgumentException("setSelectedIndex: " + anIndex + " out of bounds");
  385.         setSelectedItem(dataModel.getElementAt(anIndex));
  386.     }
  387.  
  388.     /**
  389.      * Returns the index of the currently selected item in the list. The result is not
  390.      * always defined if the JComboBox box allows selected items that are not in the
  391.      * list. Returns -1 if the receiving JComboBox has no selected item or if the 
  392.      * selected item is not in the list of items.
  393.      
  394.      * @return an int specifying the currently selected list item, where 0 specifies
  395.      *                the first item in the list, or -1 if no item is selected or if
  396.      *                the currently selected item is not in the list
  397.      */
  398.     public int getSelectedIndex() {
  399.         Object sObject = dataModel.getSelectedItem();
  400.         int i,c;
  401.         Object obj;
  402.  
  403.         for ( i=0,c=dataModel.getSize();i<c;i++ ) {
  404.             obj = dataModel.getElementAt(i);
  405.             if ( obj.equals(sObject) )
  406.                 return i;
  407.         }
  408.         return -1;
  409.     }
  410.  
  411.     /** 
  412.      * Adds an item to the item list.
  413.      * This method works only if the JComboBox uses the default data model.
  414.      * JComboBox uses the default data model when created with the 
  415.      * empty constructor and no other model has been set.
  416.      *
  417.      * @param anObject the Object to add to the list
  418.      */
  419.     public void addItem(Object anObject) {
  420.         checkDefaultComboBoxModel();
  421.         ((DefaultComboBoxModel)dataModel).addObject(anObject);
  422.     }
  423.  
  424.     /** 
  425.      * Inserts an item into the item list at a given index. 
  426.      * This method works only if the JComboBox uses the default data model.
  427.      * JComboBox uses the default data model when created with the 
  428.      * empty constructor and no other model has been set.
  429.      *
  430.      * @param anObject the Object to add to the list
  431.      * @param index    an int specifying the position at which to add the item
  432.      */
  433.     public void insertItemAt(Object anObject, int index) {
  434.         checkDefaultComboBoxModel();
  435.         ((DefaultComboBoxModel)dataModel).insertObjectAt(anObject,index);
  436.     }
  437.  
  438.     /** 
  439.      * Removes an item from the item list.
  440.      * This method works only if the JComboBox uses the default data model.
  441.      * JComboBox uses the default data model when created with the empty constructor
  442.      * and no other model has been set.
  443.      *
  444.      * @param anObject  the object to remove from the item list
  445.      */
  446.     public void removeItem(Object anObject) {
  447.         int index;
  448.         checkDefaultComboBoxModel();
  449.         index = ((DefaultComboBoxModel)dataModel).getIndexOf(anObject);
  450.         ((DefaultComboBoxModel)dataModel).removeObject(anObject);
  451.         if ( !isEditable() && dataModel.getSize() > 0 && index < dataModel.getSize() ) {
  452.             setSelectedIndex(index);
  453.         }
  454.     }
  455.  
  456.     /**  
  457.      * Removes the item at <code>anIndex</code>
  458.      * This method works only if the JComboBox uses the default data model.
  459.      * JComboBox uses the default data model when created with the 
  460.      * empty constructor and no other model has been set.
  461.      *
  462.      * @param anIndex  an int specifying the idex of the item to remove, where 0
  463.      *                 indicates the first item in the list
  464.      */
  465.     public void removeItemAt(int anIndex) {
  466.         checkDefaultComboBoxModel();
  467.         ((DefaultComboBoxModel)dataModel).removeObjectAt(anIndex);
  468.     }
  469.  
  470.     /** 
  471.      * Removes all items from the item list.
  472.      * This method works only if the JComboBox uses the default data model.
  473.      * JComboBox uses the default data model when created with the empty constructor
  474.      * and no other model has been set.
  475.      */
  476.     public void removeAllItems() {
  477.         checkDefaultComboBoxModel();
  478.         ((DefaultComboBoxModel)dataModel).removeAllObjects();
  479.         if ( !isEditable() )
  480.             setSelectedItem(null);
  481.     }
  482.  
  483.     void checkDefaultComboBoxModel() {
  484.         if ( !(dataModel instanceof DefaultComboBoxModel) )
  485.             throw new InternalError("Cannot use this method with a custom data model.");
  486.     }
  487.  
  488.     /** Causes the combo box to display its popup window **/
  489.     public void showPopup() {
  490.         getUI().showPopup();
  491.     }
  492.  
  493.     /** Causes the combo box to close its popup window **/
  494.     public void hidePopup() {
  495.         getUI().hidePopup();
  496.     }
  497.  
  498.     /** Selection **/
  499.  
  500.     /** 
  501.      * Adds an ItemListener. <code>aListener</code> will receive an event when
  502.      * the selected item changes.
  503.      *
  504.      * @param aListener  the ItemListener that is to be notified
  505.      */
  506.     public void addItemListener(ItemListener aListener) {
  507.         listenerList.add(ItemListener.class,aListener);
  508.     }
  509.  
  510.     /** Removes an ItemListener
  511.      *
  512.      * @param aListener  the ItemListener to remove
  513.      */
  514.     public void removeItemListener(ItemListener aListener) {
  515.         listenerList.remove(ItemListener.class,aListener);
  516.     }
  517.  
  518.     /** 
  519.      * Adds an ActionListener. The listener will receive an action event
  520.      * the user finishes making a selection.
  521.      *
  522.      * @param l  the ActionListener that is to be notified
  523.      */
  524.     public void addActionListener(ActionListener l) {
  525.         listenerList.add(ActionListener.class,l);
  526.     }
  527.  
  528.     /** Removes an ActionListener 
  529.      *
  530.      * @param l  the ActionListener to remove
  531.      */
  532.     public void removeActionListener(ActionListener l) {
  533.         listenerList.remove(ActionListener.class,l);
  534.     }
  535.  
  536.     /** 
  537.      * Sets the action commnand that should be included in the event
  538.      * sent to action listeners.
  539.      *
  540.      * @param aCommand  a string containing the "command" that is sent
  541.      *                  to action listeners. The same listener can then
  542.      *                  do different things depending on the command it
  543.      *                  receives.
  544.      */
  545.     public void setActionCommand(String aCommand) {
  546.         actionCommand = aCommand;
  547.     }
  548.  
  549.     /** 
  550.      * Returns the action commnand that is included in the event sent to
  551.      *  action listeners.
  552.      *
  553.      * @return  the string containing the "command" that is sent
  554.      *          to action listeners.
  555.      */
  556.     public String getActionCommand() {
  557.         return actionCommand;
  558.     }
  559.  
  560.     /**
  561.      * Notify all listeners that have registered interest for
  562.      * notification on this event type.
  563.      *  
  564.      * @see EventListenerList
  565.      */
  566.     protected void fireItemStateChanged(ItemEvent e) {
  567.         // Guaranteed to return a non-null array
  568.         Object[] listeners = listenerList.getListenerList();
  569.         // Process the listeners last to first, notifying
  570.         // those that are interested in this event
  571.         for ( int i = listeners.length-2; i>=0; i-=2 ) {
  572.             if ( listeners[i]==ItemListener.class ) {
  573.                 // Lazily create the event:
  574.                 // if (changeEvent == null)
  575.                 // changeEvent = new ChangeEvent(this);
  576.                 ((ItemListener)listeners[i+1]).itemStateChanged(e);
  577.             }
  578.         }
  579.     }   
  580.  
  581.     /**
  582.      * Notify all listeners that have registered interest for
  583.      * notification on this event type.
  584.      *  
  585.      * @see EventListenerList
  586.      */
  587.     protected void fireActionEvent() {
  588.         ActionEvent e = null;
  589.         // Guaranteed to return a non-null array
  590.         Object[] listeners = listenerList.getListenerList();
  591.         // Process the listeners last to first, notifying
  592.         // those that are interested in this event
  593.         for ( int i = listeners.length-2; i>=0; i-=2 ) {
  594.             if ( listeners[i]==ActionListener.class ) {
  595.                 if ( e == null )
  596.                     e = new ActionEvent(this,ActionEvent.ACTION_PERFORMED,getActionCommand());
  597.                 ((ActionListener)listeners[i+1]).actionPerformed(e);
  598.             }
  599.         }
  600.     }
  601.  
  602.     /**
  603.      * This method is called when the selected item changes. Its default implementation
  604.      *  notifies the item listeners
  605.      */
  606.     protected void selectedItemChanged() {
  607.         if ( selectedItemReminder != null ) {
  608.             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
  609.                                                selectedItemReminder,
  610.                                                ItemEvent.DESELECTED));
  611.         }
  612.  
  613.         selectedItemReminder = getModel().getSelectedItem();
  614.  
  615.         if ( selectedItemReminder != null )
  616.             fireItemStateChanged(new ItemEvent(this,ItemEvent.ITEM_STATE_CHANGED,
  617.                                                selectedItemReminder,
  618.                                                ItemEvent.SELECTED));
  619.         fireActionEvent();
  620.     }
  621.  
  622.     /** 
  623.      * Returns an array containing the selected item. This method is implemented for 
  624.      * compatibility with ItemSelectable.
  625.      *
  626.      * @returns an array of Objects containing one element -- the selected item
  627.      */
  628.     public Object[] getSelectedObjects() {
  629.         Object selectedObject = getSelectedItem();
  630.         if ( selectedObject == null )
  631.             return new Object[0];
  632.         else {
  633.             Object result[] = new Object[1];
  634.             result[0] = selectedObject;
  635.             return result;
  636.         }
  637.     }
  638.  
  639.     /** This method is public as an implementation side effect. 
  640.      *  do not call or override. 
  641.      */
  642.     public void actionPerformed(ActionEvent e) {
  643.         Object newItem = getEditor().getItem();
  644.         getModel().setSelectedItem(newItem);
  645.         getUI().hidePopup();
  646.     }
  647.  
  648.     /** This method is public as an implementation side effect. 
  649.      *  do not call or override. 
  650.      *
  651.      * @see com.sun.java.swing.event.ListDataListener
  652.      */
  653.     public void contentsChanged(ListDataEvent e) {
  654.         ComboBoxModel mod = getModel();
  655.         Object newSelectedItem = mod.getSelectedItem();
  656.  
  657.         /* If we are using the default model, and we are not editable
  658.          *  let's make sure that we do not have an empty selection
  659.          */
  660.         if ( !isEditable() && mod instanceof DefaultComboBoxModel &&
  661.              newSelectedItem == null && getModel().getSize() > 0 ) {
  662.             setSelectedIndex(0); 
  663.         }
  664.         else {
  665.             if ( selectedItemReminder == null ) {
  666.                 if ( newSelectedItem != null )
  667.                     selectedItemChanged();
  668.             }
  669.             else {
  670.                 if ( !selectedItemReminder.equals(newSelectedItem) )
  671.                     selectedItemChanged();
  672.             }
  673.         }
  674.  
  675.         /* If we are not editable and the selected item is no longer in
  676.          *  the list, select the next one 
  677.          */
  678.         if ( !isEditable() && newSelectedItem != null ) {
  679.             int i,c;
  680.             boolean shouldResetSelectedItem = true;
  681.             Object o;
  682.             Object selectedItem = mod.getSelectedItem();
  683.  
  684.             for ( i=0,c=mod.getSize();i<c;i++ ) {
  685.                 o = mod.getElementAt(i);
  686.                 if ( o.equals(selectedItem) ) {
  687.                     shouldResetSelectedItem = false;
  688.                     break;
  689.                 }
  690.             }
  691.  
  692.             if ( shouldResetSelectedItem ) {
  693.                 if ( mod.getSize() > 0 )
  694.                     setSelectedIndex(0);
  695.                 else
  696.                     setSelectedItem(null);
  697.             }
  698.         }
  699.     }
  700.  
  701.     /**
  702.      * Selects the list item that correponds to the specified keyboard character
  703.      * and returns true, if there is an item corresponding to that character.
  704.      * Otherwise, returns false.
  705.      *
  706.      * @param keyChar a char, typically this is a keyboard key typed by the user
  707.      */
  708.     public boolean selectWithKeyChar(char keyChar) {
  709.         int index;
  710.  
  711.         if ( keySelectionManager == null )
  712.             keySelectionManager = createDefaultKeySelectionManager();
  713.  
  714.         index = keySelectionManager.selectionForKey(keyChar,getModel());
  715.         if ( index != -1 ) {
  716.             setSelectedIndex(index);
  717.             return true;
  718.         }
  719.         else
  720.             return false;
  721.     }
  722.  
  723.     /**
  724.      * Invoked items have been added to the internal data model.
  725.      * The "interval" includes the first and last values added. 
  726.      *
  727.      * @see com.sun.java.swing.event.ListDataListener
  728.      */
  729.     public void intervalAdded(ListDataEvent e) {
  730.         contentsChanged(e);
  731.     }
  732.  
  733.     /**
  734.      * Invoked when values have been removed from the data model. 
  735.      * The"interval" includes the first and last values removed. 
  736.      *
  737.      * @see com.sun.java.swing.event.ListDataListener
  738.      */
  739.     public void intervalRemoved(ListDataEvent e) {
  740.         contentsChanged(e);
  741.     }
  742.  
  743.     /**
  744.      * Enables the combo box so that items can be selected. When the
  745.      * combo box is disabled, items cannot be selected and values
  746.      * cannot be typed into its field (if it is editable).
  747.      *
  748.      * @param b a boolean value, where true enables the component and
  749.      *          false disables it
  750.      * @beaninfo
  751.      *    preferred: true
  752.      *  description: Whether the combo box is enabled.
  753.      */
  754.     public void setEnabled(boolean b) {
  755.         super.setEnabled(b);
  756.         firePropertyChange( "enabled", !isEnabled(), isEnabled() );
  757.     }
  758.  
  759.     /**
  760.      * Initializes the editor with the specified item.
  761.      *                                 
  762.      * @param anEditor the ComboBoxEditor that displays the list item in the
  763.      *                 combo box field and allows it to be edited
  764.      * @param anItem   the object to display and edit in the field
  765.      */
  766.     public void configureEditor(ComboBoxEditor anEditor,Object anItem) {
  767.         anEditor.setItem(anItem);
  768.     }
  769.  
  770.     /**
  771.      * Handles KeyEvents, looking for the Tab key. If the Tab key is found,
  772.      * the popup window is closed.
  773.      *
  774.      * @param e  the KeyEvent containing the keyboard key that was pressed  
  775.      */
  776.     public void processKeyEvent(KeyEvent e) {
  777.         if ( e.getKeyCode() == KeyEvent.VK_TAB ) {
  778.             hidePopup();
  779.         }
  780.         super.processKeyEvent(e);
  781.     }
  782.  
  783.     /**
  784.      * Returns true if the component can receive the focus. In this case,
  785.      * the component returns false if it is editable, so that the Editor
  786.      * object receives the focus, instead of the component.
  787.      *
  788.      * @return true if the component can receive the focus, else false. 
  789.      */
  790.     public boolean isFocusTraversable() {
  791.         return getUI().isFocusTraversable();
  792.     }
  793.  
  794.     /**
  795.      * Sets the object that translates a keyboard character into a list
  796.      * selection. Typically, the first selection with a matching first
  797.      * character becomes the selected item.
  798.      *
  799.      * @beaninfo
  800.      *       expert: true
  801.      *  description: The objects that changes the selection when a key is pressed.
  802.      */
  803.     public void setKeySelectionManager(KeySelectionManager aManager) {
  804.         keySelectionManager = aManager;
  805.     }
  806.  
  807.     /**
  808.      * Returns the list's key-selection manager.
  809.      *
  810.      * @return the KeySelectionManager currently in use
  811.      */
  812.     public KeySelectionManager getKeySelectionManager() {
  813.         return keySelectionManager;
  814.     }
  815.  
  816.     /* Accessing the model */
  817.     /**
  818.      * Returns the number of items in the list.
  819.      *
  820.      * @return an int equal to the number of items in the list
  821.      */
  822.     public int getItemCount() {
  823.         return dataModel.getSize();
  824.     }
  825.  
  826.     /**
  827.      * Returns the list item at the specified index.
  828.      *
  829.      * @param index  an int indicating the list position, where the first
  830.      *               item starts at zero
  831.      * @return the Object at that list position
  832.      */
  833.     public Object getItemAt(int index) {
  834.         return dataModel.getElementAt(index);
  835.     }
  836.  
  837.     /**
  838.      * Returns an instance of the default key-selection manager.
  839.      *
  840.      * @return the KeySelectionManager currently used by the list
  841.      * @see #setKeySelectionManager
  842.      */
  843.     protected KeySelectionManager createDefaultKeySelectionManager() {
  844.         return new DefaultKeySelectionManager();
  845.     }
  846.  
  847.     /**
  848.      * Returns true to indicate that this component paints every pixel
  849.      * in its range. (In other words, it does not have a transparent
  850.      * background or foreground.)
  851.      *
  852.      * @see JComponent#isOpaque
  853.      */
  854.     public boolean isOpaque() {
  855.         return true;
  856.     }
  857.  
  858.     /**
  859.      * Default data model for a combo box.
  860.      * <p>
  861.      * Warning: serialized objects of this class will not be compatible with
  862.      * future swing releases.  The current serialization support is appropriate
  863.      * for short term storage or RMI between Swing1.0 applications.  It will
  864.      * not be possible to load serialized Swing1.0 objects with future releases
  865.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  866.      * baseline for the serialized form of Swing objects.
  867.      */
  868.     class DefaultComboBoxModel extends AbstractListModel implements ComboBoxModel, Serializable {
  869.         Vector objects = new Vector();
  870.         Object selectedObject;
  871.  
  872.         public DefaultComboBoxModel() {
  873.         }
  874.         public DefaultComboBoxModel(final Object items[]) {
  875.             int i,c;
  876.             for ( i=0,c=items.length;i<c;i++ )
  877.                 objects.addElement(items[i]);
  878.         }
  879.  
  880.         public DefaultComboBoxModel(Vector v) {
  881.             int i,c;
  882.             for ( i=0,c=v.size();i<c;i++ )
  883.                 objects.addElement(v.elementAt(i));
  884.         }
  885.  
  886.         public void setSelectedItem(Object anObject) {
  887.             selectedObject = anObject;
  888.             fireContentsChanged(this, -1, -1);
  889.         }
  890.  
  891.         public Object getSelectedItem() {
  892.             return selectedObject;
  893.         }
  894.  
  895.         public int getSize() {
  896.             return objects.size();
  897.         }
  898.  
  899.         public Object getElementAt(int index) {
  900.             if ( index >= 0 && index < objects.size() )
  901.                 return objects.elementAt(index);
  902.             else
  903.                 return null;
  904.         }
  905.  
  906.         public int getIndexOf(Object anObject) {
  907.             return objects.indexOf(anObject);
  908.         }
  909.  
  910.         void addObject(Object anObject) {
  911.             objects.addElement(anObject);
  912.             fireIntervalAdded(this,objects.size()-1, objects.size()-1);
  913.         }
  914.  
  915.         void insertObjectAt(Object anObject,int index) {
  916.             objects.insertElementAt(anObject,index);
  917.             fireIntervalAdded(this, index, index);
  918.         }
  919.  
  920.         void removeObjectAt(int index) {
  921.             objects.removeElementAt(index);
  922.             fireIntervalRemoved(this, index, index);
  923.         }
  924.  
  925.         void removeObject(Object anObject) {
  926.             int index = objects.indexOf(anObject);
  927.             if ( index != -1 ) {
  928.                 removeObjectAt(index);
  929.             }
  930.         }
  931.  
  932.         void removeAllObjects() {
  933.             int firstIndex = 0;
  934.             int lastIndex = objects.size()-1;
  935.             objects.removeAllElements();
  936.             fireIntervalRemoved(this, firstIndex, lastIndex);
  937.         }
  938.     }
  939.  
  940.  
  941.     /**
  942.      * The interface that defines a KeySelectionManager. To qualify as
  943.      * a KeySelectionManager, the class needs to implement the method
  944.      * that identifies the list index given a character and the 
  945.      * combo box data model.
  946.      */
  947.     public interface KeySelectionManager {
  948.         /** Given <code>aKey</code> and the model, returns the row
  949.          *  that should become selected. Return -1 if no match was
  950.          *  found. 
  951.          *
  952.          * @param  aKey  a char value, usually indicating a keyboard key that
  953.          *               was pressed
  954.          * @param aModel a ComboBoxModel -- the component's data model, containing
  955.          *               the list of selectable items 
  956.          * @return an int equal to the selected row, where 0 is the
  957.          *         first item and -1 is none. 
  958.          */
  959.         int selectionForKey(char aKey,ComboBoxModel aModel);
  960.     }
  961.  
  962.     class DefaultKeySelectionManager implements KeySelectionManager, Serializable {
  963.         public int selectionForKey(char aKey,ComboBoxModel aModel) {
  964.             int i,c;
  965.             int currentSelection = -1;
  966.             Object selectedItem = aModel.getSelectedItem();
  967.             String v;
  968.             String pattern;
  969.  
  970.             if ( selectedItem != null ) {
  971.                 selectedItem = selectedItem.toString();
  972.  
  973.                 for ( i=0,c=aModel.getSize();i<c;i++ ) {
  974.                     if ( selectedItem.equals(aModel.getElementAt(i).toString()) ) {
  975.                         currentSelection  =  i;
  976.                         break;
  977.                     }
  978.  
  979.                 }
  980.             }
  981.  
  982.             pattern = ("" + aKey).toLowerCase();
  983.             aKey = pattern.charAt(0);
  984.  
  985.             for ( i = ++currentSelection, c = aModel.getSize() ; i < c ; i++ ) {
  986.                 v = aModel.getElementAt(i).toString().toLowerCase();
  987.                 if ( v.length() > 0 && v.charAt(0) == aKey )
  988.                     return i;
  989.             }
  990.  
  991.             for ( i = 0 ; i < currentSelection ; i ++ ) {
  992.                 v = aModel.getElementAt(i).toString().toLowerCase();
  993.                 if ( v.length() > 0 && v.charAt(0) == aKey )
  994.                     return i;
  995.             }
  996.             return -1;
  997.         }
  998.     }
  999.  
  1000. ///////////////////
  1001. // Accessiblity support
  1002. ///////////////////
  1003.  
  1004.     /**
  1005.      * Get the AccessibleContext associated with this JComponent
  1006.      *
  1007.      * @return the AccessibleContext of this JComponent
  1008.      */
  1009.     public AccessibleContext getAccessibleContext() {
  1010.         if ( accessibleContext == null ) {
  1011.             accessibleContext = new AccessibleJComboBox();
  1012.         }
  1013.         return accessibleContext;
  1014.     }
  1015.  
  1016.     /**
  1017.      * The class used to obtain the accessible role for this object.
  1018.      * <p>
  1019.      * Warning: serialized objects of this class will not be compatible with
  1020.      * future swing releases.  The current serialization support is appropriate
  1021.      * for short term storage or RMI between Swing1.0 applications.  It will
  1022.      * not be possible to load serialized Swing1.0 objects with future releases
  1023.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  1024.      * baseline for the serialized form of Swing objects.
  1025.      */
  1026.     protected class AccessibleJComboBox extends AccessibleJComponent {
  1027.  
  1028.         /**
  1029.          * Returns the number of accessible children in the object.  If all
  1030.          * of the children of this object implement Accessible, than this
  1031.          * method should return the number of children of this object.  For
  1032.          * the JComboBox, this will return 1 if the JComboBox is not editable
  1033.          * and 2 if the JComboBox is editable and the editor is Accessible.
  1034.          *
  1035.          * @return the number of accessible children in the object.
  1036.          */
  1037.         public int getAccessibleChildrenCount() {
  1038.             if ( isEditable() 
  1039.                  && (getEditor().getEditorComponent() instanceof Accessible) ) {
  1040.                 return 2;
  1041.             }
  1042.             else {
  1043.                 return 1;
  1044.             }
  1045.         }
  1046.  
  1047.         /**
  1048.          * Return the nth Accessible child of the object.  The JList 
  1049.          * associated with the JComboBox will always be returned for
  1050.          * an index of 0.  If the JComboBox is editable and the editor
  1051.          * is Accessible, the editor will be returned for an index of 1.
  1052.          * Otherwise, null will be returned for all other indeces.
  1053.          *
  1054.          * @param i zero-based index of child
  1055.          * @return the nth Accessible child of the object
  1056.          * @see JComboBox#isEditable
  1057.          * @see JComboBox#getEditor
  1058.          */
  1059.         public Accessible getAccessibleChild(int i) {
  1060.             // force the combo box to create its components.  If this
  1061.             // is not done, the returned children can be null.
  1062.             Dimension force = getUI().getPreferredSize(JComboBox.this);
  1063.  
  1064.             Accessible ret = null;
  1065.             if ( i == 0 ) {
  1066.                 ret = new AccessibleJComboBoxList(JComboBox.this);
  1067.             }
  1068.             else if ( (i == 1) && isEditable() ) {
  1069.                 Component e = getEditor().getEditorComponent();
  1070.                 if ( e instanceof Accessible ) {
  1071.                     ret = (Accessible) e;
  1072.                 }
  1073.             }
  1074.             if ( ret != null ) {
  1075.                 AccessibleContext ac = ret.getAccessibleContext();
  1076.                 if ( ac != null ) {
  1077.                     ac.setAccessibleParent(JComboBox.this);
  1078.                 }
  1079.             }
  1080.             return ret;
  1081.         }
  1082.  
  1083.         protected class AccessibleJComboBoxList extends AccessibleContext
  1084.         implements Accessible, AccessibleComponent {
  1085.             private JComboBox parent = null;
  1086.             private AccessibleContext listAC = null;
  1087.             private AccessibleComponent listACmp = null;
  1088.  
  1089.             public AccessibleJComboBoxList(JComboBox parent) {
  1090.                 this.parent = parent;
  1091.                 this.setAccessibleParent(parent);
  1092.                 listAC = parent.getUI().getList().getAccessibleContext();
  1093.                 listACmp = listAC.getAccessibleComponent();
  1094.             }
  1095.  
  1096.             // Accessible Methods
  1097.  
  1098.             public AccessibleContext getAccessibleContext() {
  1099.                 return this;
  1100.             }
  1101.  
  1102.             // AccessibleContext methods
  1103.  
  1104.             public String getAccessibleName() {
  1105.                 return listAC.getAccessibleName();
  1106.             }
  1107.  
  1108.             public void setAccessibleName(String s) {
  1109.                 listAC.setAccessibleName(s);
  1110.             }
  1111.  
  1112.             public String getAccessibleDescription() {
  1113.                 return listAC.getAccessibleDescription();
  1114.             }
  1115.  
  1116.             public void setAccessibleDescription(String s) {
  1117.                 listAC.setAccessibleDescription(s);
  1118.             }
  1119.  
  1120.             public AccessibleRole getAccessibleRole() {
  1121.                 return listAC.getAccessibleRole();
  1122.             }
  1123.  
  1124.             public AccessibleStateSet getAccessibleStateSet() {
  1125.                 return listAC.getAccessibleStateSet();
  1126.             }
  1127.  
  1128.             public int getAccessibleIndexInParent() {
  1129.                 return 0;
  1130.             }
  1131.  
  1132.             public int getAccessibleChildrenCount() {
  1133.                 return listAC.getAccessibleChildrenCount();
  1134.             }
  1135.  
  1136.             public Accessible getAccessibleChild(int i) {
  1137.                 Accessible a = listAC.getAccessibleChild(i);
  1138.                 AccessibleContext ac = a.getAccessibleContext();
  1139.                 if ( ac != null ) {
  1140.                     ac.setAccessibleParent(this);
  1141.                 }
  1142.                 return a;
  1143.             }
  1144.  
  1145.             public Locale getLocale() {
  1146.                 return listAC.getLocale();
  1147.             }
  1148.  
  1149.             public void addPropertyChangeListener(PropertyChangeListener l) {
  1150.                 listAC.addPropertyChangeListener(l);
  1151.             }
  1152.  
  1153.             public void removePropertyChangeListener(PropertyChangeListener l) {
  1154.                 listAC.removePropertyChangeListener(l);
  1155.             }
  1156.  
  1157.             public AccessibleAction getAccessibleAction() {
  1158.                 return listAC.getAccessibleAction();
  1159.             }
  1160.  
  1161.             public AccessibleComponent getAccessibleComponent() {
  1162.                 return this; // to override getBounds() and getLocation...();
  1163.             }
  1164.  
  1165.             public AccessibleSelection getAccessibleSelection() {
  1166.                 return listAC.getAccessibleSelection();
  1167.             }
  1168.  
  1169.             public AccessibleText getAccessibleText() { 
  1170.                 return listAC.getAccessibleText();
  1171.             }
  1172.  
  1173.             public AccessibleValue getAccessibleValue() {
  1174.                 return listAC.getAccessibleValue();
  1175.             }
  1176.  
  1177.  
  1178.             // AccessibleComponent methods
  1179.  
  1180.             public Color getBackground() {
  1181.                 return listACmp.getBackground();
  1182.             }
  1183.  
  1184.             public void setBackground(Color c) {
  1185.                 listACmp.setBackground(c);
  1186.             }
  1187.  
  1188.             public Color getForeground() {
  1189.                 return listACmp.getForeground();
  1190.             }
  1191.  
  1192.             public void setForeground(Color c) {
  1193.                 listACmp.setForeground(c);
  1194.             }
  1195.  
  1196.             public Cursor getCursor() {
  1197.                 return listACmp.getCursor();
  1198.             }
  1199.  
  1200.             public void setCursor(Cursor c) {
  1201.                 listACmp.setCursor(c);
  1202.             }
  1203.  
  1204.             public Font getFont() {
  1205.                 return listACmp.getFont();
  1206.             }
  1207.  
  1208.             public void setFont(Font f) {
  1209.                 listACmp.setFont(f);
  1210.             }
  1211.  
  1212.             public FontMetrics getFontMetrics(Font f) {
  1213.                 return listACmp.getFontMetrics(f);
  1214.             }
  1215.  
  1216.             public boolean isEnabled() {
  1217.                 return listACmp.isEnabled();
  1218.             }
  1219.  
  1220.             public void setEnabled(boolean b) {
  1221.                 listACmp.setEnabled(b);
  1222.             }
  1223.  
  1224.             public boolean isVisible() {
  1225.                 return listACmp.isVisible();
  1226.             }
  1227.  
  1228.             public void setVisible(boolean b) {
  1229.                 listACmp.setVisible(b);
  1230.             }
  1231.  
  1232.             public boolean isShowing() {
  1233.                 return listACmp.isShowing();
  1234.             }
  1235.  
  1236.             public boolean contains(Point p) {
  1237.                 return parent.contains(p);
  1238.             }
  1239.  
  1240.             public Point getLocationOnScreen() {
  1241.                 return parent.getLocationOnScreen();
  1242.             }
  1243.  
  1244.             public Point getLocation() {
  1245.                 return new Point(0,0);
  1246.             }
  1247.  
  1248.             public void setLocation(Point p) {
  1249.                 // nothing
  1250.             }
  1251.  
  1252.             public Rectangle getBounds() {
  1253.                 Rectangle r = parent.getBounds();
  1254.                 r.x = 0;
  1255.                 r.y = 0;
  1256.                 return r;
  1257.             }
  1258.  
  1259.             public void setBounds(Rectangle r) {
  1260.                 // nothing
  1261.             }
  1262.  
  1263.             public Dimension getSize() {
  1264.                 return parent.getSize();
  1265.             }
  1266.  
  1267.             public void setSize (Dimension d) {
  1268.                 // nothing
  1269.             }
  1270.  
  1271.             public Accessible getAccessibleAt(Point p) {
  1272.                 return listACmp.getAccessibleAt(p);
  1273.             }
  1274.  
  1275.             public boolean isFocusTraversable() {
  1276.                 return listACmp.isFocusTraversable();
  1277.             }
  1278.  
  1279.             public void requestFocus() {
  1280.                 parent.requestFocus();
  1281.             }
  1282.  
  1283.             public void addFocusListener(FocusListener l) {
  1284.                 listACmp.addFocusListener(l);
  1285.             }
  1286.  
  1287.             public void removeFocusListener(FocusListener l) {
  1288.                 listACmp.removeFocusListener(l);
  1289.             }
  1290.         } // inner class AccessibleJComboBoxList
  1291.  
  1292.         /**
  1293.          * Get the state set of this object.
  1294.          *
  1295.          * @return an instance of AccessibleState containing the current state 
  1296.          * of the object
  1297.          * @see AccessibleState
  1298.          */
  1299.         public AccessibleStateSet getAccessibleStateSet() {
  1300.             AccessibleStateSet states = super.getAccessibleStateSet();
  1301.             if ( isEditable() ) {
  1302.                 states.add(AccessibleState.EDITABLE);
  1303.             }
  1304.             return states;
  1305.         }
  1306.  
  1307.         public Accessible getAccessibleAt(Point p) {
  1308.             Accessible a = getAccessibleChild(1);
  1309.             if ( a != null ) {
  1310.                 return a; // the editor
  1311.             }
  1312.             else {
  1313.                 return getAccessibleChild(0); // the list
  1314.             }
  1315.         }
  1316.  
  1317.     } // innerclass AccessibleJComboBox
  1318. }
  1319.